package full_search;

import env.Action;
import env.Enviroment;
import env.Tile;
import java.util.LinkedList;
import java.util.List;

/*************************************************************
 * ************************************************************
 * NOTE: FOR NOW, g=depth always!!!
 * @author user
 *************************************************************
 *************************************************************/
public class FullNode {
    static int counter=0;

    //public static Enviroment env;
    private FullNode parent;
    private int depth;
    private int g; //path cost FOR NOW, g=depth always!!!
    private Action action; //what got me here?
    // FullState:
    private FullState state;
    int id;
    int h; //heuristic
    int f; //only for A*

    public FullNode(FullNode parent, Action action, int depth, int g, FullState state) {
        this.parent = parent;
        this.action = action;
        this.depth = depth;
        this.g = g;
        this.state=state;
        
        this.id=counter++;
        
        
        h=h1();
        if(state.isAlive) {
             h+=(remainingFlags()>1?remainingFlags():0);
        }
        else {
            h+=Enviroment.FLAG_REWARD*remainingFlags(); //punishment for death!
          // System.err.println("oi");
        }
        f=h+g;
        //System.out.println("created " +this);
    }

    public LinkedList<Action> getActionPath() {
        LinkedList<Action> ans = new LinkedList<Action>();
        String s="";
        int j=18;
        for (FullNode i = this; i.parent != null; i = i.parent) {
            s=j+")\t"+i+" ->\n"+s;
            j--;
            ans.addFirst(i.action);
        }
        System.out.println("node path:\n" +s);
        return ans;
    }

    public int getCol() {
        return state.getCol();
    }

    public int getRow() {
        return state.getRow();
    }

    public int getDepth() {
        return depth;
    }

    public int getG() {
        return g;
    }
    
    public FullState getState() {
        return this.state;
    }
    
    public int remainingFlags(){
        return state.getFlags().size();
    }
    
    
    private int abs(int x) {
        return x >= 0 ? x : -x;
    }
       //h(n) = manhattan distance from nearest flag.
    private int h1() {
        if(remainingFlags()==0) //BUG FIX:  if it's 0, h1() will return MAX_VALUE
            return 0;
        int d = Integer.MAX_VALUE;
        int row=getRow(), col=getCol();
        for (Tile flag : getState().getFlags()) {
            int temp = abs(row - flag.getRow()) + abs(col - flag.getCol());
            if (temp < d) {
                d = temp;
            }
        }
        return d;
    }
    
    //h3(n) = manhattan distance from Farthest  flag!
    private int h3() {
        int d = 0;
        for (Tile flag : getState().getFlags()) {
            int temp = abs(getRow() - flag.getRow()) + abs(getCol() - flag.getCol());
            if (temp > d) {
                d = temp;
            }
        }
        return d;
    }
    
    //h(n) = manhattan distance from ALL flags.
    private int h2() {
        int d = 0;
        for (Tile flag : getState().getFlags()) {
            d+= abs(getRow() - flag.getRow()) + abs(getCol() - flag.getCol());
        }
        return d;
    }
    
    //h(n) = manhattan distance from SOME flag.
    private int h4() {
        if(remainingFlags()==0) //BUG FIX:  if it's 0, h1() will return MAX_VALUE
            return 0;
        int d = Integer.MAX_VALUE;
        for (Tile flag : getState().getFlags()) {
            int temp = abs(getRow() - flag.getRow()) + abs(getCol() - flag.getCol());
            if (temp < d) {
                d = temp;
            }
        }
        return d;
    }
    

    //returns 0 to 3 new nodes
    public List<FullNode> expand() {
        List<FullNode> ans = new LinkedList<FullNode>();
        
        if(!state.isAlive)
            return ans; //no moves for the dead man...
        
        FullState fwd=state.clone(action.fwd);
        //BUG: this was a nasty bug: i was treating env as a constant, while MY AGENT would be moving!
        //if (Enviroment.env.isCellFree(fwd.getRow(), fwd.getCol())) {
        
        if (!Enviroment.env.map[fwd.getRow()][fwd.getCol()].isWall() &&
                //to stop sentry collisions:
                (state.getSentry()==null || fwd.getRow()!=state.getSentry().getRow() || fwd.getCol()!=state.getSentry().getCol())) { 
            //BUG: TODO: the following commented lines cause non-optimal results: why?
//             if(fwd.capture)
//                 ans.add(new FullNode(this, action.fwd, depth + 1, g + 1 - Enviroment.FLAG_REWARD, fwd)); //F
//             else
                 ans.add(new FullNode(this, action.fwd, depth + 1, g + 1, fwd)); //F
        }
        //Can't turn on ice:
        if (!Enviroment.env.map[state.getRow()][state.getCol()].isIce()) {
            ans.add(new FullNode(this, action.left, depth + 1, g + 1, state.clone(action.left))); //L
            ans.add(new FullNode(this, action.right, depth + 1, g + 1, state.clone(action.right))); //R
        }
        
        return ans;
    }

    public boolean equals(FullNode e) {
        return state.equals(e.state);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FullNode other = (FullNode) obj;
        if (this.state != other.state && (this.state == null || !this.state.equals(other.state))) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + (this.state != null ? this.state.hashCode() : 0);
        return hash;
    }

    @Override
    public String toString() {
        return "node #"+id+" after "+action+" from #"+(parent==null?"":parent.id)+"\tg="+g+", h="+h+", f="+(g+h)+", "+state;
    }
    
    
    

}
